﻿// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// 
// Copyright (c) 2012, Dr. Masroor Ehsan
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//    * Redistributions of source code must retain the above copyright
//      notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above copyright
//      notice, this list of conditions and the following disclaimer in the
//      documentation and/or other materials provided with the distribution.
//    * Neither the name of the <organization> nor the
//      names of its contributors may be used to endorse or promote products
//      derived from this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 
// $Id: MainForm.cs 19 2012-01-28 13:26:06Z masroore@gmail.com $
// 
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using EasyRenderer;
using LWObjects;
using ReportGeneratorManager;
using Xceed.Grid;

namespace LWEditor
{
    public partial class MainForm : Form
    {
        private readonly DbCatalogManager.DbCatalogManager _dbCatalogManager;
        private readonly ReportManager _reportManager;
        private IEnumerable<OrderableTest> _currentFilteredCatalog;

        public MainForm()
        {
            _reportManager = new ReportManager();
            _reportManager.LoadExtensions(Utils.GetReportsExtensionsDir());

            _dbCatalogManager = new DbCatalogManager.DbCatalogManager();
            _dbCatalogManager.LoadExtensions(Utils.GetDbExtensionsDir());

            InitializeComponent();

            var render = new EasyRender();
            render.Toolstrip.Curve = 2;
            /*
            var backBlend = new Blend();
            backBlend.Positions = new float[] { 0, 0.1f, 0.4f, 0.8f, 1 };
            backBlend.Factors = new float[] { 0, 0.3f, 0.6f, 0.7f, 0.2f };
            render.Toolstrip.BackgroundBlend = backBlend;
            */
            render.SmoothText = true;
            ToolStripManager.Renderer = render;

            CreateDetailGrids();

            TestPrefixGroupMap.Instance.LoadFromFile(Utils.PREFIX_MAP_FILE);
            BillableItemsCatalog.Instance.LoadFromDefinitionFile(Utils.BILLABLE_ITEMS_DEFINITION_FILE);

            //AlaFileReader.ReadFile("in.ala");
            //BiccFileReader.ReadFile("bicc.ala");

            LoadTestGroupsListbox();
            lbTestGroups.SelectedIndex = 0;
        }

        private void CreateDetailGrid(string title, string[] columnNames, int[] columnWidths)
        {
            var detailGrid = new DetailGrid {Collapsed = true, Title = title, ShowTreeLines = false, ReadOnly = true};

            //var titleResultGrid = new TextRow(title);
            //detailGrid.HeaderRows.Add(titleResultGrid);

            detailGrid.FixedColumnSplitter.Visible = false;
            detailGrid.HeaderRows.Add(new ColumnManagerRow());

            for (var i = 0; i < columnNames.Count(); i++)
            {
                var index = detailGrid.Columns.Add(new Column(columnNames[i], typeof (string)));
                detailGrid.Columns[index].Width = columnWidths[i];
                detailGrid.Columns[index].CanBeSorted = false;
            }

            grdTestDetails.DetailGridTemplates.Add(detailGrid);
            grdTestDetails.SynchronizeDetailGrids = true;
        }

        private void CreateDetailGrids()
        {
            var RCColumnNames = new[] {"Parameters", "Code", "Ref. Range"};
            var RCColumnWidths = new[] {280, 60, 100};
            var BIColumnNames = new[] {"Billable Items", "Code", "Price"};
            var BIColumnWidths = new[] {320, 60, 60};

            CreateDetailGrid("Result Codes", RCColumnNames, RCColumnWidths);
            CreateDetailGrid("Billable Items", BIColumnNames, BIColumnWidths);
        }

        private void LoadTestGroupsListbox()
        {
            lbTestGroups.Items.Clear();
            lbTestGroups.Items.Add("<ALL>");
            foreach (string lab in TestPrefixGroupMap.Instance.Values)
            {
                lbTestGroups.Items.Add(lab);
            }
        }

        private void SetCurrentFolder(string folder)
        {
            openFileDialog1.InitialDirectory = folder;
            saveFileDialog1.InitialDirectory = folder;
            openFileDialog1.FileName = string.Empty;
            saveFileDialog1.FileName = string.Empty;
            openFileDialog1.FilterIndex = 1;
            saveFileDialog1.FilterIndex = 1;
        }

        private void LoadTestsInGrid(IEnumerable<OrderableTest> tests)
        {
            _currentFilteredCatalog = tests.OrderBy(x=> x.Lab).ToList();

            grdTestDetails.DataRows.Clear();
            foreach (var atest in _currentFilteredCatalog)
            {
                var row = grdTestDetails.DataRows.AddNew();
                row.Cells["colTestName"].Value = atest.Name;
                row.Cells["colTestCode"].Value = atest.TestId;
                row.Cells["colTestPrice"].Value = atest.ListPrice;
                row.Cells["colAccessionType"].Value = atest.AccessionType;

                row.Cells["colIsActive"].Value = (atest.InactiveDate == DateTime.MinValue);
                row.Cells["colIsActive"].Tag = "Active?";
                row.Cells["colIsActive"].MouseEnter += ShowTip;

                row.Cells["colHasRCs"].Value = atest.ResultCodes.Count > 0;
                row.Cells["colHasRCs"].Tag = "Has Result codes?";
                row.Cells["colHasRCs"].MouseEnter += ShowTip;

                row.Cells["colHasBIs"].Value = atest.BillableItems.Count > 0;
                row.Cells["colHasBIs"].Tag = "Has Billable Items?";
                row.Cells["colHasBIs"].MouseEnter += ShowTip;

                row.Cells["colIsProfile"].Value = atest.IsProfile;
                row.Cells["colIsProfile"].Tag = "Is Profile?";
                row.Cells["colIsProfile"].MouseEnter += ShowTip;

                row.Cells["colTestSequence"].Value = atest.ReportSequence;
                row.Cells["colTestSequence"].Tag = "Report Sequence";
                row.Cells["colTestSequence"].MouseEnter += ShowTip;

                row.Tag = atest;
                row.EndEdit();
            }

            stCatalogTotal.Text = string.Format(@"Total Number of Test(s): {0}", OrderableTestCatalog.Instance.Count);
            stLabTotal.Text = string.Format(@"Test(s) in Lab: {0}", _currentFilteredCatalog.Count());
        }

        private void ShowTip(object sender, EventArgs e)
        {
            var cell = (DataCell) sender;
            if (cell.Tag != null)
                toolTip1.SetToolTip(grdTestDetails, ((DataCell) sender).Tag.ToString());
        }

        private void btnLoadAlaFile_Click(object sender, EventArgs e)
        {
            openFileDialog1.Filter = Utils.FILTER_STRING_ALA;
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                AlaFileReader.ReadFile(openFileDialog1.FileName);
                SetCurrentFolder(Path.GetDirectoryName(openFileDialog1.FileName));
                lbTestGroups_SelectedIndexChanged(this, null);
            }
        }

        private void FilterTestsGridByTestGroup(string lab)
        {
            IOrderedEnumerable<OrderableTest> tests;
            if (lab == "<ALL>")
            {
                tests = OrderableTestCatalog.Instance.Values.OrderBy(t => t.TestId).ThenBy(t => t.Name);
            }
            else
            {
                tests = from t in OrderableTestCatalog.Instance.Values
                        where t.Lab == lab
                        orderby t.TestId , t.Name
                        select t;
            }
            LoadTestsInGrid(tests);
        }

        private void lbTestGroups_SelectedIndexChanged(object sender, EventArgs e)
        {
            btnSearch.Checked = false;
            var group = lbTestGroups.SelectedItem.ToString();
            FilterTestsGridByTestGroup(group);
        }

        private void btnEditBillableItems_Click(object sender, EventArgs e)
        {
            using (var frm = new BillabeItemsEditForm())
            {
                frm.ShowDialog();
            }
        }

        private void EditCurrentlySelectedTest()
        {
            if ((grdTestDetails.SelectedRows.Count == 1) && (grdTestDetails.SelectedRows[0] != null))
            {
                var atest = (OrderableTest) grdTestDetails.SelectedRows[0].Tag;
                using (var frm = new OrderableTestEditForm())
                {
                    frm.InitControlsWithTestData(atest);
                    if (frm.ShowDialog() == DialogResult.OK)
                    {
                        OrderableTestCatalog.Instance[frm.CurrentTest.TestId] = frm.CurrentTest;
                        lbTestGroups_SelectedIndexChanged(this, null);
                    }
                }
            }
        }

        private void btnAddNewTest_Click(object sender, EventArgs e)
        {
            AddNewTest();
        }

        private void AddNewTest()
        {
            using (var frm = new OrderableTestEditForm())
            {
                frm.InitNewTestWithGroup(lbTestGroups.SelectedIndex > 0
                                             ? lbTestGroups.SelectedItem.ToString()
                                             : string.Empty);

                if (frm.ShowDialog() == DialogResult.OK)
                {
                    OrderableTestCatalog.Instance.Add(frm.CurrentTest.TestId, frm.CurrentTest);
                    lbTestGroups_SelectedIndexChanged(this, null);
                }
            }
        }

        private void btnEditTest_Click(object sender, EventArgs e)
        {
            EditCurrentlySelectedTest();
        }

        private void btnDeleteTest_Click(object sender, EventArgs e)
        {
            DeleteCurrentlySelectedTest();
        }

        private void DeleteCurrentlySelectedTest()
        {
#if IMPLEMENT            
            if (lvwTestDetails.SelectedObject != null)
            {
                if (MessageBox.Show(@"Really delete?", @"Confirm", MessageBoxButtons.YesNo) == DialogResult.Yes)
                {
                    var test = (OrderableTest) lvwTestDetails.SelectedObject;
                    OrderableTestCatalog.Instance.Remove(test.TestId);
                    lbTestGroups_SelectedIndexChanged(this, null);
                }
            }
#endif
        }

        private void btnSaveAlaFile_Click(object sender, EventArgs e)
        {
            if (OrderableTestCatalog.Instance.Count == 0)
            {
                MessageBox.Show("Empty test catalog");
                return;
            }
            
            saveFileDialog1.Filter = Utils.FILTER_STRING_ALA;
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                AlaFileWriter.WriteFile(saveFileDialog1.FileName);
                SetCurrentFolder(Path.GetDirectoryName(saveFileDialog1.FileName));
            }
        }

        private void btnGenerateBiccFile_Click(object sender, EventArgs e)
        {
            if (OrderableTestCatalog.Instance.Count == 0)
            {
                MessageBox.Show("Empty test catalog");
                return;
            }
            
            saveFileDialog1.Filter = Utils.FILTER_STRING_BICC;
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                BiccFileWriter.WriteFile(saveFileDialog1.FileName);
                SetCurrentFolder(Path.GetDirectoryName(saveFileDialog1.FileName));
            }
        }

        private void btnLoadBiccFile_Click(object sender, EventArgs e)
        {
            openFileDialog1.Filter = Utils.FILTER_STRING_BICC;
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                BiccFileReader.ReadFile(openFileDialog1.FileName);
                SetCurrentFolder(Path.GetDirectoryName(openFileDialog1.FileName));
                lbTestGroups_SelectedIndexChanged(this, null);
            }
        }

        private void btnSaveDb_Click(object sender, EventArgs e)
        {
            saveFileDialog1.Filter = Utils.FILTER_STRING_DB;
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                _dbCatalogManager.SaveCatalog(saveFileDialog1.FileName);
                SetCurrentFolder(Path.GetDirectoryName(saveFileDialog1.FileName));
            }
        }

        private void btnLoadDb_Click(object sender, EventArgs e)
        {
            openFileDialog1.Filter = Utils.FILTER_STRING_DB;
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                _dbCatalogManager.LoadCatalog(openFileDialog1.FileName);
                SetCurrentFolder(Path.GetDirectoryName(openFileDialog1.FileName));
                lbTestGroups_SelectedIndexChanged(this, null);
            }
        }

        private void MainForm_KeyUp(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.F2:
                    EditCurrentlySelectedTest();
                    break;
                case Keys.Delete:
                case Keys.F8:
                    DeleteCurrentlySelectedTest();
                    break;
                case Keys.Insert:
                case Keys.F9:
                    AddNewTest();
                    break;
                case Keys.F:
                    if (ModifierKeys == Keys.Control)
                    {
                        btnSearch_Click(this, null);
                    }
                    break;
            }
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            Text = @"Masroor's Quick'n'Dirty Test Catalog Editor - v" + Utils.APP_VERSION;
            //ToolStripManager.Renderer = new Office2007Renderer.Office2007Renderer();
        }

        private void btnPrintPreview_Click(object sender, EventArgs e)
        {
        }

        private void ShowPrintPreview(bool dotMatrix, bool detailed)
        {
            if (_currentFilteredCatalog.Count() == 0)
            {
                MessageBox.Show(@"Nothing to print");
                return;
            }

            _reportManager.GenerateReport(_currentFilteredCatalog, dotMatrix, detailed);
        }

        private void btnSearch_Click(object sender, EventArgs e)
        {
            if (OrderableTestCatalog.Instance.Count == 0)
            {
                MessageBox.Show(@"Nothing to search");
                return;
            }

            if (btnSearch.Checked)
            {
                btnSearch.Checked = false;
                return;
            }

            using (var frm = new SearchForm())
            {
                if (frm.ShowDialog() == DialogResult.OK)
                {
                    IEnumerable<OrderableTest> tests = FilterTestCatalog(frm.SearchString, frm.SearchTestId,
                                                                         frm.MatchCase,
                                                                         frm.RegularExpression);

                    if (tests.Count() > 0)
                    {
                        LoadTestsInGrid(tests);
                        btnSearch.Checked = true;
                    }
                    else
                    {
                        MessageBox.Show(@"No matching tests found");
                    }
                }
            }
        }

        private static IEnumerable<OrderableTest> FilterTestCatalog(string searchTerm, bool serachTestId, bool matchCase,
                                                                    bool regexSearch)
        {
            IEnumerable<OrderableTest> tests;
            if (serachTestId)
            {
                // filter by test id
                if (regexSearch)
                {
                    var rx = matchCase ? new Regex(searchTerm) : new Regex(searchTerm, RegexOptions.IgnoreCase);
                    tests = from t in OrderableTestCatalog.Instance.Values
                            let matches = rx.Matches(t.TestId)
                            where matches.Count > 0
                            orderby t.TestId , t.Name
                            select t;
                }
                else
                {
                    if (matchCase)
                    {
                        tests = from t in OrderableTestCatalog.Instance.Values
                                where t.TestId == searchTerm
                                orderby t.TestId , t.Name
                                select t;
                    }
                    else
                    {
                        tests = from t in OrderableTestCatalog.Instance.Values
                                where t.TestId.ToUpper() == searchTerm
                                orderby t.TestId , t.Name
                                select t;
                    }
                }
            }
            else
            {
                // filter by Test Name
                if (regexSearch)
                {
                    var rx = matchCase ? new Regex(searchTerm) : new Regex(searchTerm, RegexOptions.IgnoreCase);
                    tests = from t in OrderableTestCatalog.Instance.Values
                            let matches = rx.Matches(t.Name)
                            where matches.Count > 0
                            orderby t.Name , t.TestId
                            select t;
                }
                else
                {
                    if (matchCase)
                    {
                        tests = from t in OrderableTestCatalog.Instance.Values
                                where t.Name.Contains(searchTerm)
                                orderby t.Name , t.TestId
                                select t;
                    }
                    else
                    {
                        tests = from t in OrderableTestCatalog.Instance.Values
                                where t.Name.ToUpper().Contains(searchTerm)
                                orderby t.Name , t.TestId
                                select t;
                    }
                }
            }
            return tests;
        }

        private void btnSearch_CheckedChanged(object sender, EventArgs e)
        {
            if (!btnSearch.Checked)
            {
                lbTestGroups_SelectedIndexChanged(this, null);
            }
        }

        private void btnPrintDotMatrix_Click(object sender, EventArgs e)
        {
            ShowPrintPreview(true, false);
        }

        private void grdTestDetails_InitializingDetailGrid(object sender, InitializingDetailGridEventArgs e)
        {
            if (e.Grid.ParentDataRow.DetailGrids.Count == 2)
            {
                //e.Grid.DataRowTemplate.AutoHeightMode = AutoHeightMode.Minimum;
                //e.Grid.DataRowTemplate.FitHeightToEditors = false;

                var atest = (OrderableTest) e.Grid.ParentDataRow.Tag;

                if (btnExpand.Checked)
                {
                    e.Grid.ParentDataRow.DetailGrids[0].Collapsed = atest.ResultCodes.Count == 0;
                    e.Grid.ParentDataRow.DetailGrids[1].Collapsed = atest.BillableItems.Count == 0;
                }

                foreach (var code in atest.ResultCodes)
                {
                    if (code != null)
                    {
                        var row = e.Grid.ParentDataRow.DetailGrids[0].DataRows.AddNew();
                        row.Cells[0].Value = code.Result.Name;
                        row.Cells[1].Value = code.Result.Code;
                        row.Cells[2].Value = code.Result.ReferenceRange;
                        row.CanBeSelected = false;
                        row.EndEdit();
                    }
                }

                foreach (var item in atest.BillableItems)
                {
                    if (item != null)
                    {
                        var row = e.Grid.ParentDataRow.DetailGrids[1].DataRows.AddNew();
                        row.Cells[0].Value = item.Name;
                        row.Cells[1].Value = item.Code;
                        row.Cells[2].Value = item.Price;
                        row.CanBeSelected = false;
                        row.EndEdit();
                    }
                }
            }
        }

        private void grdTestDetails_AddingDataRow(object sender, AddingDataRowEventArgs e)
        {
        }

        private void btnCollapse_Click(object sender, EventArgs e)
        {
            var shouldCollapse = !((ToolStripButton) sender).Checked;
            grdTestDetails.BeginInit();
            try
            {
                foreach (DataRow dataRow in grdTestDetails.DataRows)
                {
                    foreach (DetailGrid detailGrid in dataRow.DetailGrids)
                    {
                        if (shouldCollapse)
                        {
                            detailGrid.Collapse();
                        }
                        else
                        {
                            detailGrid.Expand();
                        }
                    }
                }
            }
            finally
            {
                grdTestDetails.EndInit();
            }
        }

        private void grdTestDetails_DoubleClick(object sender, EventArgs e)
        {
            EditCurrentlySelectedTest();
        }

        private void btnPrintTestsList_Click(object sender, EventArgs e)
        {
            ShowPrintPreview(false, false);
        }

        private void miPrintDetailedReport_Click(object sender, EventArgs e)
        {
            ShowPrintPreview(false, true);
        }
    }
}